#include "../header/evhttp/httpserver.h"
#include "../header/net/net_info.h"

#include <stdlib.h>
pthread_t execute_thread;
void run_http_server(int port){

    //create libevent base event base
    struct event_base *base = event_base_new();
    //create libevent http event
    struct evhttp *http = evhttp_new(base);
    //handler no defined request
    evhttp_set_gencb(http,http_request_cb,NULL);
    //execute orders on server
    evhttp_set_cb(http,"/order",order_request_cb,NULL);
    //get server disk info
    evhttp_set_cb(http,"/disk",disk_request_cb,NULL);
    //get server memory info
    evhttp_set_cb(http,"/mem",mem_request_cb,NULL);
    //get server CPUs info
    evhttp_set_cb(http,"/cpu",cpus_request_cb,NULL);
    //get server process info
    evhttp_set_cb(http,"/process",process_request_cb,NULL);
    //get start time
    evhttp_set_cb(http,"/start_time",start_time_request_cb,NULL);
    //get  cpu cores number;
    evhttp_set_cb(http,"/cores",cores_request_cb,NULL);
    //get  net info;
    evhttp_set_cb(http,"/net_info",net_request_cb,NULL);
    //bind ip and port
    evhttp_bind_socket(http,"0.0.0.0",port);
    //loop requests
    event_base_dispatch(base);
    event_base_free(base);
    evhttp_free(http);
}

void net_request_cb(struct evhttp_request *req, void *arg){
    long rec,rev;
    get_net_rate(&rec,&rev);
    char net_info[256];
    sprintf(net_info,"{\"rec\":%ld,\"rev\":%ld}",rec,rev);
    response_msg(req,net_info,HTTP_OK);
}

void http_request_cb(struct evhttp_request *req, void *arg){
    evhttp_send_error(req,HTTP_NOTFOUND,"the path you requests not defined!");
}

void cpus_request_cb(struct evhttp_request *req, void *arg){
    double cpu_usage = get_cpu_usage();
    char cpu_usage_percent[20];
    sprintf(cpu_usage_percent,"{\"usage\":\"%.2f\"}",cpu_usage);
    response_msg(req,cpu_usage_percent,HTTP_OK);
}
void process_request_cb(struct evhttp_request *req, void *arg){
    cJSON* process_json = process_info();
    char* process = cJSON_PrintUnformatted(process_json);
    cJSON_Delete(process_json);
    response_msg(req,process,HTTP_OK);
}


void mem_request_cb(struct evhttp_request *req, void *arg){
    char* mem_str = get_mem_json_str();
    response_msg(req,mem_str,HTTP_OK);
}
char* get_mem_json_str(){
    char start_time[100];
    struct sysinfo info;
    sys_start_time(start_time,&info);
    char mem_info_str[6][30];
    memset(mem_info_str,'\0', sizeof(mem_info_str)+1);
    sprintf(mem_info_str[0],"%ld",info.totalram);
    sprintf(mem_info_str[1],"%ld",info.freeram);
    sprintf(mem_info_str[2],"%ld",info.totalswap);
    sprintf(mem_info_str[3],"%ld",info.freeswap);
    sprintf(mem_info_str[4],"%d",info.procs);
    sprintf(mem_info_str[5],"%d",info.mem_unit);
    cJSON *mem_info;
    mem_info = cJSON_CreateObject();
    cJSON_AddStringToObject(mem_info, "totalram",mem_info_str[0]);
    cJSON_AddStringToObject(mem_info, "freeram",mem_info_str[1]);
    cJSON_AddStringToObject(mem_info, "totalswap",mem_info_str[2]);
    cJSON_AddStringToObject(mem_info, "freeswap",mem_info_str[3]);
    cJSON_AddStringToObject(mem_info, "procs",mem_info_str[4]);
    cJSON_AddStringToObject(mem_info, "mem_unit",mem_info_str[5]);
    char *mem_str = cJSON_PrintUnformatted(mem_info);
    return mem_str;

}
//获取硬盘信息
void disk_request_cb(struct evhttp_request *req, void *arg){
    char* cjson;
    cjson = disk_info();
    response_msg(req,cjson,HTTP_OK);

}

void start_time_request_cb(struct evhttp_request *req, void *arg){
    char start_time[50];
    struct sysinfo info;
    sys_start_time(start_time,&info);
    char ret_start_time[100];
    sprintf(ret_start_time,"{\"start_time\":\"%s\"}",start_time);
    response_msg(req,ret_start_time,HTTP_OK);
}
void cores_request_cb(struct evhttp_request *req, void *arg){
    int cores = get_cpu_cores();
    char rev_cores[50];
    sprintf(rev_cores,"{\"cores\":%d}",cores);
    response_msg(req,rev_cores,HTTP_OK);
}

//执行命令
void order_request_cb(struct evhttp_request *req, void *arg){
    if(token_compare(req)!=0){
        response_msg(req,"token not equeals",HTTP_BADREQUEST);
        return ;
    }

    char *command;
    command = get_request_input(req);
    printf("command: %s \n",command);
    order_response(req,command);
    free(command);
}

void sig_exc_alarm(int sig){
    int kill_ret = pthread_kill(execute_thread,0);
   if(kill_ret == 0){
        pthread_cancel(execute_thread);
   }
}
int token_compare(struct evhttp_request *req){
    struct evkeyvalq *headers;
    const char *token;
    headers = evhttp_request_get_input_headers(req);
    token = evhttp_find_header(headers,"TOKEN");
    char *TOKEN = "ABC";
    int ret = strcmp(token,TOKEN);
    printf("\ntoken : %s compare result:%d\n",token,ret);
    return ret;
}

void response_msg(struct evhttp_request *req, const char *msg,int code){
    struct evbuffer *evbuff = evbuffer_new();
    evbuffer_add_printf(evbuff,msg);
    evhttp_send_reply(req,code,"OK",evbuff);
    evbuffer_free(evbuff);
}

void order_response(struct evhttp_request *req,const char* command){
    alarm(0);
    if(command!=NULL&&strlen(command)>0){
        alarm(3);
        signal(SIGALRM,sig_exc_alarm);
        if(pthread_create(&execute_thread,NULL,(void*)execute_command,(void*)command) !=0 ){
            perror("thread create error:");
            response_msg(req,"system create thread error",HTTP_OK);
        }
        int ret;
        if(pthread_join(execute_thread,  (void*)&ret) !=0 ){
                perror("thread join error:");
                printf("execute error\n");
                response_msg(req,"system error reback error",HTTP_OK);
            }else{
                printf("execute success %d\n",ret);
                if(ret==0){
                    response_msg(req,command,HTTP_OK);
                }else{
                    response_msg(req,"command executed time out",HTTP_OK);
                }
            }
    }
}


void* execute_command(void* arg){
    pthread_testcancel();
    char *command_result_buffer = NULL;
    FILE *command_f;
    command_f = popen((char*)arg,"r");
    if(command_f!=NULL){
        char buffer[1024];
        int pos = 0;
        while(fgets(buffer,1024,command_f)!=NULL){
            int len = strlen(buffer);
            command_result_buffer = (char *)realloc(command_result_buffer,pos+len);
            memcpy(command_result_buffer+pos, buffer, len);
            pos += len;
        }
        sprintf(arg,"%s",command_result_buffer);
        free(command_result_buffer);
        pclose(command_f);
        return (void*)0;
    }else {
        sprintf(arg,"%s","command executed failed");
        return (void*)0;
    }
}

char* get_request_input(struct evhttp_request *req){
    struct evbuffer *evb = evbuffer_new();
    evb = evhttp_request_get_input_buffer(req);
    char command[131072] ;
    memset(command,'\0',131072);

    while (evbuffer_get_length(evb)) {
        int n;
        char cbuf[1024];
        memset(cbuf,'\0',1024);
        n = evbuffer_remove(evb, cbuf, sizeof(cbuf));
        if (n > 0){
            strcat(command,cbuf);
        }
    }
    char* exe_command;
    exe_command = (char*)malloc(strlen(command)+1);
    return exe_command;
}
void remote_read_callback(struct evhttp_request* remote_rsp, void* arg){
    //fprintf(stderr, "request failed\n");
    event_base_loopexit((struct event_base*)arg, NULL);
}
int read_header_done_callback(struct evhttp_request* remote_rsp, void* arg)
{
    //fprintf(stderr, "read_header_done_callback\n");
    //fprintf(stderr, "< HTTP/1.1 %d %s\n", evhttp_request_get_response_code(remote_rsp), evhttp_request_get_response_code_line(remote_rsp));
    struct evkeyvalq* headers = evhttp_request_get_input_headers(remote_rsp);
    struct evkeyval* header;
    TAILQ_FOREACH(header, headers, next)
    {
        fprintf(stderr, "< %s: %s\n", header->key, header->value);
    }
    //fprintf(stderr, "< \n\n");
    return 0;
}

void read_chunk_callback(struct evhttp_request* remote_rsp, void* arg)
{
    //fprintf(stderr, "read_chunk_callback\n");
    char buf[4096];
    struct evbuffer* evbuf = evhttp_request_get_input_buffer(remote_rsp);
    int n = 0;
    while ((n = evbuffer_remove(evbuf, buf, 4096)) > 0)
    {
        fwrite(buf, n, 1, stdout);
    }
}

void remote_request_error_callback(enum evhttp_request_error error, void* arg)
{
    //fprintf(stderr, "remote_request_error_callback\n");
    event_base_loopexit((struct event_base*)arg, NULL);
}

void remote_connection_close_callback(struct evhttp_connection* connection, void* arg)
{
    //fprintf(stderr, "remote connection closed\n");
    event_base_loopexit((struct event_base*)arg, NULL);
}


int send_sys_info(const char* url){



        cJSON *postDataJson,*process_json,*connection_info;


        char* disk_info_1 = disk_info();


        char start_time[100];
        struct sysinfo info;
        sys_start_time(start_time,&info);
        char os_name_version[256];
        sys_release(os_name_version);
    #ifdef __x86_64__
        strcat(os_name_version,"@x86_64");
    #elif __i386__
        strcat(os_name_version,"@i386");
    #endif

        int cores = sysconf(_SC_NPROCESSORS_ONLN);
        char cpu_cores[10];
        sprintf(cpu_cores,"%d",cores);
        double cpu_usage = get_cpu_usage();
        char cpu_usage_str[15];
        sprintf(cpu_usage_str,"%.2f",cpu_usage);
        char* mem_str = get_mem_json_str();

        long rec,rec1,rev,rev1,tcp_connections;
        tcp_connections=rec=rec1=rev=rev1=0;

        get_net_rate(&rec,&rev);
        sleep(1);
        get_net_rate(&rec1,&rev1);

        char net_info[256];
        connection_info =  get_net_connections(&tcp_connections);
        sprintf(net_info,"{\"rev\":%ld,\"send\":%ld,\"tcp_num\":%d}",(rec1-rec),(rev1-rev),tcp_connections);
        //printf("net_info:%s\n",net_info);

        process_json = process_info();
        char* proc  = cJSON_PrintUnformatted(process_json);
        postDataJson = cJSON_CreateObject();
        char* connectsion_str = cJSON_PrintUnformatted(connection_info);
        cJSON_AddStringToObject(postDataJson, "start_time",start_time);
        cJSON_AddStringToObject(postDataJson, "os_name_version",os_name_version);
        cJSON_AddStringToObject(postDataJson, "cpu_usage",cpu_usage_str);
        cJSON_AddStringToObject(postDataJson, "process",proc);
        cJSON_AddStringToObject(postDataJson, "disk_info",disk_info_1);
        cJSON_AddStringToObject(postDataJson, "cores",cpu_cores);
        cJSON_AddStringToObject(postDataJson, "mem_usage",mem_str);
        cJSON_AddStringToObject(postDataJson, "net_info",net_info);
        cJSON_AddStringToObject(postDataJson, "connections",connectsion_str);//connection_info
        char* sys_info_json = cJSON_PrintUnformatted(postDataJson);
        //printf("%s\n",sys_info_json);
	    struct evhttp_uri* uri = evhttp_uri_parse(url);
	    if (!uri)
	    {
			fprintf(stderr, "parse url failed!\n");
			return 1;
	    }

	    struct event_base* base = event_base_new();
	    if (!base)
	    {
			fprintf(stderr, "create event base failed!\n");
			return 1;
	    }

	    struct evdns_base* dnsbase = evdns_base_new(base, 1);
	    if (!dnsbase)
	    {
			fprintf(stderr, "create dns base failed!\n");
			return 1;
	    }
	    assert(dnsbase);

	    struct evhttp_request* request = evhttp_request_new(remote_read_callback, base);
	    evhttp_request_set_header_cb(request, read_header_done_callback);
	    evhttp_request_set_chunked_cb(request, read_chunk_callback);
	    evhttp_request_set_error_cb(request, remote_request_error_callback);

	    const char* host = evhttp_uri_get_host(uri);
	    if (!host)
	    {
		fprintf(stderr, "parse host failed!\n");
		return 1;
	    }

	    int port = evhttp_uri_get_port(uri);
	    if (port < 0) port = 80;

	    const char* request_url = url;
	    const char* path = evhttp_uri_get_path(uri);
	    if (path == NULL || strlen(path) == 0)
	    {
			request_url = "/";
	    }

	    //printf("url:%s host:%s port:%d path:%s request_url:%s\n", url, host, port, path, request_url);

	    struct evhttp_connection* connection =  evhttp_connection_base_new(base, dnsbase, host, port);
	    if (!connection)
	    {
		fprintf(stderr, "create evhttp connection failed!\n");
		return 1;
	    }
        evhttp_connection_set_timeout(connection,5);
	    evhttp_connection_set_closecb(connection, remote_connection_close_callback, base);
	    evhttp_add_header(evhttp_request_get_output_headers(request), "Host", host);
	    evbuffer_add(request->output_buffer, sys_info_json, strlen(sys_info_json));
        evhttp_make_request(connection, request, EVHTTP_REQ_POST, request_url);
	    event_base_dispatch(base);

        if(connection){
            evhttp_connection_free(connection);
        }

        if(base){
            event_base_free(base);
        }

        free(disk_info_1);
        free(sys_info_json);
        free(proc);
        free(mem_str);
        free(connectsion_str);
        cJSON_Delete(process_json);
        cJSON_Delete(postDataJson);
        //cJSON_Delete(connection_info);
    return 1;
}
